#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <memory.h>
#include <rpc/pmap_clnt.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <execinfo.h>

#include "rpc.h"
#include "svc_fun.h"

#ifndef SIG_PF
#define SIG_PF void(*)(int)
#endif
#define STACK_SIZE 20

SVC_FUN svc_fun_table[SVC_FUN_NUM] = {
	{1,svc_read_file},
};

static void show_stack(int level);
static struct timeval TIMEOUT = { 250, 0 };

void *  rpc_req(char * host, int fun_id, void* data, int len)
{
	static RPC_PACK  req_reply;

	CLIENT *clnt = NULL;
	char * usr_data = NULL;
	char*  ret_data = NULL;
	RPC_PACK  req_arg;

	memset(&req_arg, 0, sizeof(RPC_PACK));
	memset((char *)&req_reply, 0, sizeof(req_reply));

	usr_data = (char *)malloc(len);
	ret_data = (char *)malloc(len);
	memcpy(usr_data, data, len);

	req_arg.fun_id = fun_id;
	req_arg.clnt_id = 188;
	req_arg.usr_data_len = len;
	req_arg.usr_data_val = usr_data;
	show_stack(3);
	clnt = clnt_create (host, PACK, VERSION, "tcp");
	if (clnt == NULL) {
		clnt_pcreateerror (host);
		exit (1);
	}

	if (clnt_call (clnt, RPC_DISPATCH,
		(xdrproc_t) xdr_RPC_PACK, (caddr_t) &req_arg,
		(xdrproc_t) xdr_RPC_PACK, (caddr_t) &req_reply,
		TIMEOUT) != RPC_SUCCESS) {
		clnt_perror (clnt, "call failed");
		return 0;
	}

	memcpy(ret_data, req_reply.usr_data_val, req_reply.usr_data_len);

	clnt_destroy (clnt);
	return ret_data;
}

void *rpc_dispatch(RPC_PACK *argp, struct svc_req *rqstp)
{
	static RPC_PACK result;
	char *  p = NULL;
	int i = 0;
	SVC_FUN_POINT dest_fun = NULL;
	char * fun_data = NULL;
	char * reply_data = NULL;
	u_int   fun_data_len = 0;
	memset(&result, 0, sizeof(result));

	fun_data_len = argp->usr_data_len;	
	fun_data = (char * )malloc(fun_data_len);
	
	memcpy(fun_data, argp->usr_data_val, fun_data_len);

	 for(i = 0; i < SVC_FUN_NUM; i++)
	 {
	 	if(argp->fun_id == svc_fun_table[i].fun_id)
	 	{
			dest_fun = svc_fun_table[i].svc_fun;
			break;
	 	}
	 }
	if (SVC_FUN_NUM == i)
	{
	 	printf("Server Procedure not found!\n");
	 	return NULL;
	}
	p = (char *)(*dest_fun)((void *)fun_data);

	free(fun_data);

	reply_data = (char * )malloc(fun_data_len);
	memcpy(reply_data, p, fun_data_len);
	result.fun_id = 1;
	result.clnt_id = 189;
	result.usr_data_len = fun_data_len;
	result.usr_data_val = reply_data;

	return &result;
}
static void
rpc_recv(struct svc_req *rqstp, register SVCXPRT *transp)
{
	printf("recv a call .\n");
	char *result = NULL;
	RPC_PACK rpc_dispatch_arg;
	memset (&rpc_dispatch_arg, 0, sizeof (rpc_dispatch_arg));

	switch (rqstp->rq_proc) {
	case NULLPROC:
		(void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);
		return;

	case RPC_DISPATCH:
	{
		if (!svc_getargs (transp, (xdrproc_t) xdr_RPC_PACK, (caddr_t) &rpc_dispatch_arg)) {
			svcerr_decode (transp);
			return;
		}
		result = (char *)rpc_dispatch(&rpc_dispatch_arg, rqstp);
		if (result != NULL && !svc_sendreply(transp, (xdrproc_t) xdr_RPC_PACK, (caddr_t) result)) {
			svcerr_systemerr (transp);
		}
		if (!svc_freeargs (transp, (xdrproc_t) xdr_RPC_PACK, (caddr_t) &rpc_dispatch_arg)) {
			fprintf (stderr, "%s", "unable to free arguments");
			exit (1);
		}
#if 1
		free((((RPC_PACK*)result)->usr_data_val));
#endif
		break;
	}
	default:
		svcerr_noproc (transp);
		return;
	}	
	return;
}

int rpc_svc_run ()
{
	register SVCXPRT *transp;

	pmap_unset (PACK, VERSION);

	transp = svcudp_create(RPC_ANYSOCK);
	if (transp == NULL) {
		fprintf (stderr, "%s", "cannot create udp service.");
		exit(1);
	}
	if (!svc_register(transp, PACK, VERSION, rpc_recv, IPPROTO_UDP)) {
		fprintf (stderr, "%s", "unable to register (PACK, VERSION, udp).");
		exit(1);
	}

	transp = svctcp_create(RPC_ANYSOCK, 0, 0);
	if (transp == NULL) {
		fprintf (stderr, "%s", "cannot create tcp service.");
		exit(1);
	}
	if (!svc_register(transp, PACK, VERSION, rpc_recv, IPPROTO_TCP)) {
		fprintf (stderr, "%s", "unable to register (PACK, VERSION, tcp).");
		exit(1);
	}

	svc_run ();
	fprintf (stderr, "%s", "svc_run returned");
	exit (1);
	/* NOTREACHED */
}

bool_t
xdr_RPC_PACK (XDR *xdrs, RPC_PACK *objp)
{
	register int32_t *buf;

	 if (!xdr_int (xdrs, &objp->fun_id))
		 return FALSE;
	 if (!xdr_int (xdrs, &objp->clnt_id))
		 return FALSE;
	 if (!xdr_bytes (xdrs, (char **)&objp->usr_data_val, (u_int *) &objp->usr_data_len, ~0))
		 return FALSE;
	/*if (!xdr_int (xdrs, &objp->usr_data_len))
		 return FALSE;*/
	return TRUE;
}

static void show_stack(int level)
{
	int j, nptrs;	
	void *buffer[STACK_SIZE];
	char **strings;
	nptrs = backtrace(buffer, STACK_SIZE);
	printf("-------------------------------%d fun upon-------------------------------\n", nptrs);

   	
	if (nptrs > level)
	{
		nptrs = level + 1;
	}
 	strings = backtrace_symbols(buffer, nptrs);
 	if (strings == NULL) {
 	    perror("backtrace_symbols");
 	    //exit(EXIT_FAILURE);
 	}
 	
 	/*for (j = 1; j < nptrs; j++)
 	{
 	    printf("%s\n", strings[j]);	
 	}*/
 	
 	char * ps = NULL;
 	char * pe = NULL;
 	char fun[20] = {0};
 	char addr[10] = {0};
 	for (j = 1; j < nptrs; j++)
 	{
 		ps = strchr(strings[j], '(');
 		pe = strchr(strings[j], '+');
 		if(pe - ps > 1)
 		{
 			memset(fun, 0, sizeof(fun));
 			strncpy(fun, ps+1, pe - ps - 1);
 		}
 		ps = strchr(strings[j], '[');
 		pe = strchr(strings[j], ']');
 		if( pe - ps > 1)
 		{
 			memset(addr, 0, sizeof(addr));
 			strncpy(addr, ps+1 , pe - ps - 1);
 		}
 		printf("[%s:%s]\n", fun, addr); 		
 		
 	}
 	printf("------------------------------------------------------------------------\n");

 	free(strings);
}

